package kubernetes

import (
    kongConfigurationAPIV1 "github.com/kong/kubernetes-ingress-controller/pkg/apis/configuration/v1"
    kongConfigurationV1 "github.com/kong/kubernetes-ingress-controller/pkg/client/configuration/clientset/versioned/typed/configuration/v1"
    appsv1 "k8s.io/api/apps/v1"
    batchv1 "k8s.io/api/batch/v1"
    batchv1beta1 "k8s.io/api/batch/v1beta1"
    corev1 "k8s.io/api/core/v1"
    networkingv1 "k8s.io/api/networking/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    kube "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    ev1 "knative.dev/eventing/pkg/apis/eventing/v1"
    sourcev1 "knative.dev/eventing/pkg/apis/sources/v1"
    kapiV1 "knative.dev/serving/pkg/apis/serving/v1"
    kclientV1 "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1"

    "git.inspur.com/x/common/loggerfactory"
    "knative.dev/eventing/pkg/client/clientset/versioned"
)

var (
    emptyListOptions   = metav1.ListOptions{}
    emptyGetOptions    = metav1.GetOptions{}
    emptyDeleteOptions = metav1.DeleteOptions{}
    emptyCreateOptions = metav1.CreateOptions{}
    emptyUpdateOptions = metav1.UpdateOptions{}
    logger             = loggerfactory.GetLogger()
)

type PodLogs struct {
    Logs string `json:"logs,omitempty"`
}

// KnativeClientInterface for mocks (only mocked function are necessary here)
type KnativeClientInterface interface {
    GetNamespace(namespace string) (*corev1.Namespace, error)
    GetNamespaces(labelSelector string) ([]corev1.Namespace, error)
    GetServices(namespace string, selectorLabels map[string]string) ([]corev1.Service, error)
    GetDeployment(namespace, deploymentName string) (*appsv1.Deployment, error)
    GetDeployments(namespace string) ([]appsv1.Deployment, error)
    GetDeploymentsByLabel(namespace string, labelSelector string) ([]appsv1.Deployment, error)
    GetReplicaSets(namespace string) ([]appsv1.ReplicaSet, error)
    GetStatefulSet(namespace string, statefulsetName string) (*appsv1.StatefulSet, error)
    GetStatefulSets(namespace string) ([]appsv1.StatefulSet, error)
    GetReplicationControllers(namespace string) ([]corev1.ReplicationController, error)
    GetService(namespace, serviceName string) (*corev1.Service, error)
    GetEndpoints(namespace, serviceName string) (*corev1.Endpoints, error)
    GetPods(namespace, labelSelector string) ([]corev1.Pod, error)
    GetPod(namespace, name string) (*corev1.Pod, error)
    GetPodLogs(namespace, name string, opts *corev1.PodLogOptions) (*PodLogs, error)
    GetCronJobs(namespace string) ([]batchv1beta1.CronJob, error)
    GetJobs(namespace string) ([]batchv1.Job, error)
    GetConfigMap(namespace, name string) (*corev1.ConfigMap, error)
    UpdateConfigMap(namespace string, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error)

    CreateKnativeService(namespace string, service *kapiV1.Service) (*kapiV1.Service, error)
    UpdateKnativeService(namespace string, service *kapiV1.Service) (*kapiV1.Service, error)
    GetKnativeRevisionList(namespace string, labelSelector string) (*kapiV1.RevisionList, error)
    GetKnativeService(namespace, serviceName string) (*kapiV1.Service, error)
    DeleteKnativeService(namespace, serviceName string) error
    DeleteKnativeRevision(namespace, revisionName string) error

    CreateKnativeRoute(namespace string, route *kapiV1.Route) (*kapiV1.Route, error)
    GetKnativeRoute(namespace, name string) (*kapiV1.Route, error)
    UpdateKnativeRoute(namespace string, route *kapiV1.Route) (*kapiV1.Route, error)
    DeleteKnativeRoute(namespace, name string) error
    DeleteKnativeRouteByLabel(namespace string, labelSelector string) error

    GetKongClusterPlugin(name string) (*kongConfigurationAPIV1.KongClusterPlugin, error)
    UpdateKongClusterPlugin(config *kongConfigurationAPIV1.KongClusterPlugin) (*kongConfigurationAPIV1.KongClusterPlugin, error)

    CreateNetworkPolicy(namespace string, policy *networkingv1.NetworkPolicy) (*networkingv1.NetworkPolicy, error)
    GetNetworkPolicy(namespace string, policyName string) (*networkingv1.NetworkPolicy, error)
    DeleteNetworkPolicy(namespace string, policyName string) error

    GetNodes(labelSelector string) (*corev1.NodeList, error)
    GetSecrets(namespace string) (*corev1.SecretList, error)
    CreateKongPlugin(namespace string, kp *kongConfigurationAPIV1.KongPlugin) (*kongConfigurationAPIV1.KongPlugin, error)
    UpdateKongPlugin(namespace string, kp *kongConfigurationAPIV1.KongPlugin) (*kongConfigurationAPIV1.KongPlugin, error)
    DeleteKongPlugin(namespace string, kp string) error
    UpdateTrigger(namespace string, t *ev1.Trigger) (*ev1.Trigger, error)
    CreateTrigger(namespace string, t *ev1.Trigger) (*ev1.Trigger, error)
    UpdatePingsource(namespace string, ps *sourcev1.PingSource) (*sourcev1.PingSource, error)
    CreatePingsource(namespace string, ps *sourcev1.PingSource) (*sourcev1.PingSource, error)
    UpdateIngress(namespace string, ingress *networkingv1.Ingress) (*networkingv1.Ingress, error)
    CreateIngress(namespace string, ingress *networkingv1.Ingress) (*networkingv1.Ingress, error)
    GetIngress(namespace string, ingressName string) (*networkingv1.Ingress, error)
    DeleteSecret(namespace, secretName string) error
    UpdateSecret(namespace string, secret *corev1.Secret) (*corev1.Secret, error)
    CreateSecret(namespace string, secret *corev1.Secret) (*corev1.Secret, error)
    GetSecret(namespace string, secretName string) (*corev1.Secret, error)
    DeleteNamespace(namespace string) error
    CreateNamespace(namespace *corev1.Namespace) (*corev1.Namespace, error)
}

// FcClient is the client struct for Kubernetes and Istio APIs
// It hides the way it queries each API
type FcClient struct {
    KnativeClientInterface
    k8s        *kube.Clientset
    KServing   *kclientV1.ServingV1Client
    KEventing  *versioned.Clientset
    kongConfig *kongConfigurationV1.ConfigurationV1Client
}

// GetK8sApi returns the clientset referencing all K8s rest clients
func (in *FcClient) GetK8sApi() *kube.Clientset {
    return in.k8s
}

// NewClientFromConfig creates a new client to the Kubernetes and Istio APIs.
// It takes the assumption that Istio is deployed into the cluster.
// It hides the access to Kubernetes/Openshift credentials.
// It hides the low level use of the API of Kubernetes and Istio, it should be considered as an implementation detail.
// It returns an error on any problem.
func NewClientFromConfig(config *rest.Config) (*FcClient, error) {
    client := FcClient{}
    logger.Debug("Rest perf config QPS: %f Burst: %d", config.QPS, config.Burst)

    k8s, err := kube.NewForConfig(config)
    if err != nil {
        return nil, err
    }
    client.k8s = k8s

    kServing, err := kclientV1.NewForConfig(config)
    if err != nil {
        return nil, err
    }
    kEventing, err := versioned.NewForConfig(config)
    if err != nil {
        return nil, err
    }
    kongConfig, err := kongConfigurationV1.NewForConfig(config)
    client.KServing = kServing
    client.KEventing = kEventing
    client.kongConfig = kongConfig
    return &client, nil
}
